﻿using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.SharePoint;
using Kiiro.Common.Entities;
using Kiiro.Common.Utility;
using Kiiro.DataAccess;
using Kiiro.ExceptionManagement;
using Kiiro.Validation.Validators.Entities;
using Kiiro.Model.Utility;

namespace Kiiro.Model
{
    public class ProjectsModel
    {
        /// <summary>
        /// Create Project SubSite when creating a new Project
        /// </summary>
        private static bool CreateProjectSite ( Project project, bool canCreate, Guid siteCollectionId, string serverRelativeUrl )
        {
            bool provisioned = false;

            if ( ( project.PermissionBitsProjects != null &&
                project.PermissionBitsProjects.Count > 0 &&
                project.PermissionBitsProjects [ 0 ] ) ||
                canCreate )
            {
                try
                {
                    string projectTemplate = Constants.SPSiteDefinition.Name + "#" + Constants.SPSiteDefinition.ProjectId;
                    string description = project.SummaryNote ?? project.Description ?? string.Empty;
                    string webUrl = project.ServerRelativeWebUrl;

                    using ( SPWeb portalWeb = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
                    {
                        portalWeb.AllowUnsafeUpdates = true;

                        using ( SPWeb newWeb = portalWeb.Webs.Add ( webUrl, webUrl, description, 1033, projectTemplate, true, false ) )
                        {
                            provisioned = newWeb.Provisioned;
                            project.ServerRelativeWebUrl = newWeb.ServerRelativeUrl;
                        }

                        portalWeb.AllowUnsafeUpdates = false;
                    }
                }
                catch ( Exception ex )
                {
                    ExceptionManager.HandleException ( ex, true );
                }
            }
            else
            {
                throw new KiiroUnauthorizedAccessException ( "Creating Project Failed: Current User is not allowed to Create a new Project object." );
            }

            return provisioned;
        }

        /// <summary>
        /// Delete Project SubSite when deleting a Project
        /// </summary>
        private static bool DeleteProjectSite ( Project project, bool canDelete, Guid siteCollectionId, string serverRelativeUrl )
        {
            bool deleted = false;

            if ( ( project.PermissionBitsProjects != null &&
                project.PermissionBitsProjects.Count > 0 &&
                project.PermissionBitsProjects [ 3 ] ) ||
                canDelete )
            {
                try
                {
                    using ( SPWeb systemWeb = SPHelpers.GetSystemWeb ( siteCollectionId, serverRelativeUrl ) )
                    {
                        systemWeb.AllowUnsafeUpdates = true;
                        systemWeb.Delete ( );
                        systemWeb.AllowUnsafeUpdates = false;
                    }

                    using ( SPWeb systemWeb = SPHelpers.GetSystemWeb ( siteCollectionId, serverRelativeUrl ) )
                    {
                        deleted = !systemWeb.Exists;
                    }
                }
                catch ( Exception ex )
                {
                    ExceptionManager.HandleException ( ex, true );
                }
            }
            else
            {
                throw new KiiroUnauthorizedAccessException ( "Deleting Project Failed: Current User is not allowed to Delete Project object." );
            }

            return deleted;
        }

        /// <summary>
        /// Gets a Project by ProjectId
        /// </summary>
        /// <param name="projectId">The projectId to get.</param>
        /// <param name="siteCollectionId">The site collection id</param>
        /// <param name="serverRelativeUrl">The server relative url</param>
        public static Project GetProjectById ( int projectId, Guid siteCollectionId, string serverRelativeUrl )
        {
            return GetProjectsByIds (
                new List<int> { projectId }, siteCollectionId, serverRelativeUrl )
                    .FirstOrDefault ( x => x.Id == projectId );
        }

        /// <summary>
        /// Gets a list of Project entities. If projectIds is null or empty this method returns ALL Projects.
        /// </summary>
        /// <param name="projectIds">The list of project ids to get. If projectIds is null or empty this method returns ALL projects.</param>
        /// <param name="siteCollectionId">The site collection id</param>
        /// <param name="serverRelativeUrl">The server relative url</param>
        /// <returns>If projectIds is null or empty this method returns ALL projects.</returns>
        public static List<Project> GetProjectsByIds ( List<int> projectIds, Guid siteCollectionId, string serverRelativeUrl )
        {
            return GetProjectsByIds ( projectIds, siteCollectionId, serverRelativeUrl, true );
        }

        /// <summary>
        /// Gets a list of Project entities.
        /// </summary>
        /// <param name="projectIds">The list of project ids to get</param>
        /// <param name="siteCollectionId">The site collection id</param>
        /// <param name="serverRelativeUrl">The server relative url</param>
        /// <param name="ifEmptyReturnAll">If set to true, returns ALL projects when projectIds list is null or empty</param>
        public static List<Project> GetProjectsByIds ( List<int> projectIds, Guid siteCollectionId, string serverRelativeUrl, bool ifEmptyReturnAll )
        {
            return ProjectsDA.Instance.GetProjectsByIds ( projectIds, siteCollectionId, serverRelativeUrl, ifEmptyReturnAll );
        }

        /// <summary>
        /// Get Projects for currentUser
        /// </summary>
        /// <param name="siteCollectionId">The site collection id.</param>
        /// <param name="serverRelativeUrl">The server relative url.</param>
        /// <param name="currentUser">The current user as a Midori object.</param>
        /// <param name="projectStatus">A list containing the ProjectStatus types to return.</param>
        /// <returns>Projects of type projectStatus that are visible to currentUser.</returns>
        public static List<Project> GetProjectsForUser ( Guid siteCollectionId, string serverRelativeUrl, User currentUser, List<ProjectStatus> projectStatus )
        {
            List<Project> projects = new List<Project> ( );
            List<Project> userProjects = new List<Project> ( );
            List<int> projectIds = new List<int> ( );

            if ( currentUser != null && ( !currentUser.IsAdministrator && !currentUser.ManageProjectsOnly ) )
                projectIds = currentUser.ProjectIds;

            bool ifEmptyReturnAll = currentUser.IsAdministrator || currentUser.ManageProjectsOnly;

            return GetProjectsByIds ( projectIds, siteCollectionId, serverRelativeUrl, ifEmptyReturnAll )
                .Where ( x => projectStatus.Contains ( x.ProjectStatus ) ).ToList ( );
        }

        public static Project GetByServerRelativeWebUrl ( Guid siteCollectionId, string serverRelativeWebUrl )
        {
            Project project = ProjectsDA.Instance.GetByServerRelativeWebUrl ( siteCollectionId, serverRelativeWebUrl );
            if ( project != null && project.ModifiedBy != null && project.ModifiedBy.Id != default ( int ) )
                project.ModifiedBy = UserModel.GetUserById ( project.ModifiedBy.Id, siteCollectionId, serverRelativeWebUrl );
            return project;
        }

        /// <summary>
        /// Gets the total number of Active and Being Planned projects
        /// </summary>
        public static int GetNumberOfOpenProjects ( Guid siteCollectionId, string serverRelativeUrl )
        {
            return ProjectsDA.Instance.GetNumberOfOpenProjects ( siteCollectionId, serverRelativeUrl );
        }


        /// <summary>
        /// Gets the web part sandbox navigation text
        /// </summary>
        public static string GetWPSandboxNavText ( Guid siteCollectionId, string serverRelativeUrl )
        {
            Project project = GetByServerRelativeWebUrl ( siteCollectionId, serverRelativeUrl );
            return project != null ? project.WPSandboxNavText : "";
        }

        public static bool IsWPSandboxActivated ( Guid siteCollectionId, string serverRelativeUrl )
        {
            Project project = GetByServerRelativeWebUrl ( siteCollectionId, serverRelativeUrl );
            return project != null && project.IsWPSandboxActivated;
        }

        public static void UpdateProjectStatistics ( Midori midori, Guid siteCollectionId, string webUrl )
        {
            UpdateProjectStatistics ( midori, siteCollectionId, webUrl, false );
        }

        public static void UpdateProjectStatistics ( Midori midori, Guid siteCollectionId, string webUrl, bool fromTimerJob )
        {
            //•	Where you should be is based on summing the % complete of where each task should be for the project.
            //•	Where you are today is based on summing the % complete of all tasks in the project.

            try
            {
                if ( midori != null )
                {
                    Project project = null;

                    if ( midori.Projects.Count > 0 )
                    {
                        project = midori.Projects [ 0 ];
                    }
                    else
                    {
                        if ( Microsoft.SharePoint.Utilities.SPUrlUtility.IsUrlRelative ( webUrl ) )
                        {
                            project = GetByServerRelativeWebUrl ( siteCollectionId, webUrl );
                        }
                        else
                        {
                            throw new Exception ( "Unable to retrieve Project for site: " + webUrl + "." );
                        }
                    }

                    if ( project != null )
                    {
                        List<Work> tasks = new List<Work> ( );
                        List<Work> issues = new List<Work> ( );
                        List<Document> documents = new List<Document> ( );
                        List<Discussion> discussions = new List<Discussion> ( );

                        int dayOfWeekNum = ( int ) DateTime.Now.DayOfWeek;

                        #region Task and Issue Stats

                        if ( fromTimerJob )
                        {
                            List<Work> allWork = WorkModel.GetAllWork ( siteCollectionId, webUrl );
                            if ( allWork != null && allWork.Count > 0 )
                                midori.Work.AddRange ( allWork );
                        }

                        if ( midori.Work.Count > 0 || fromTimerJob )
                        {
                            // get workItem data if item was deleted
                            midori.Work.ForEach (
                                delegate ( Work work )
                                {
                                    if ( work.EditType == EditType.Deleted )
                                    {
                                        // set explicitly just in case
                                        //work.IsLateDel = true;
                                        //work.IsHighPriorityDel = work.WorkItemType == WorkType.Issue;
                                        //work.IsSlippingDel = work.WorkItemType == WorkType.Task;
                                    }
                                } );

                            tasks = midori.Work.FindAll ( Work.IsTask );
                            issues = midori.Work.FindAll ( Work.IsIssue );

                            #region Task Stats
                            if ( tasks.Count > 0 || fromTimerJob )
                            {
                                double minutesPerDay = default ( double );
                                //variable for old stats
                                float taskDurationOld = default ( float );
                                float minutesShouldCompletedOld = default ( float );
                                float projectDurationOld = default ( float );
                                float taskShouldBeOld = default ( float );
                                float taskWeightOld = default ( float );
                                float taskCompleteOnProjectOld = default ( float );
                                //float taskShouldOnProjectOld = default ( float );
                                float assignedWorkOld = default ( float );

                                //variable for new stats
                                float taskDurationNew = default ( float );
                                float minutesShouldCompletedNew = default ( float );
                                float projectDurationNew = default ( float );
                                float taskShouldBeNew = default ( float );
                                float taskWeightNew = default ( float );
                                float taskCompleteOnProjectNew = default ( float );
                                //float taskShouldOnProjectNew = default ( float );
                                float assignedWorkNew = default ( float );

                                int numLate = 0;
                                int numSlipping = 0;

                                float totalWorkCompleted = fromTimerJob ? 0 : project.ActualWork;
                                float totalWorkShouldBeCompleted = fromTimerJob ? 0 : project.ProjectedWork;
                                float totalWork = fromTimerJob ? 0 : project.TotalWork;

                                minutesPerDay = SetupModel.GetHoursPerDay ( siteCollectionId, webUrl ) * 60;

                                #region Stats Update

                                if ( project.TaskStats.Count > 0 && !fromTimerJob )
                                    int.TryParse ( project.TaskStats [ 0 ], out numLate );
                                if ( project.TaskStats.Count > 1 && !fromTimerJob )
                                    int.TryParse ( project.TaskStats [ 1 ], out numSlipping );

                                if ( !fromTimerJob )
                                {
                                    #region Work Stats To Be Removed

                                    //Get All Project Tasks
                                    //Project Duration should be all tasks duration added up
                                    foreach ( Work work in tasks )
                                    {
                                        if ( work.EditType != EditType.Created )
                                        {
                                            if ( work.EditType == EditType.Deleted )
                                            {
                                                work.BeforePercentComplete = work.PercentageComplete;
                                                work.BeforeEnd = work.EndDate;
                                                work.BeforeStart = work.StartDate;
                                                work.BeforeAssignedWork = work.AssignedWork;
                                            }
                                            if ( work.BeforeEnd != DateTime.MinValue && work.BeforeStart != DateTime.MinValue )
                                            {
                                                assignedWorkOld = default ( float );
                                                if ( work.BeforeAssignedWork == default ( float ) && work.BeforeStart != DateTime.MinValue && work.BeforeEnd != DateTime.MinValue )
                                                {
                                                    assignedWorkOld = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( work.BeforeStart, work.BeforeEnd ) * minutesPerDay );
                                                }
                                                else if ( work.BeforeAssignedWork != default ( float ) && work.BeforeStart != DateTime.MinValue && work.BeforeEnd != DateTime.MinValue )
                                                {
                                                    assignedWorkOld = work.BeforeAssignedWork;
                                                }
                                                if ( assignedWorkOld != default ( float ) )
                                                {
                                                    minutesPerDay = assignedWorkOld / DateTimeHelpers.GetNumberOfWorkDays ( work.BeforeStart, work.BeforeEnd );
                                                    projectDurationOld = assignedWorkOld;
                                                    project.TotalWork -= assignedWorkOld;
                                                }
                                            }
                                        }
                                    }
                                    foreach ( Work task in tasks )
                                    {
                                        assignedWorkOld = default ( float );
                                        if ( task.EditType != EditType.Created )
                                        {
                                            if ( task.EditType == EditType.Deleted )
                                            {
                                                task.BeforePercentComplete = task.PercentageComplete;
                                                task.BeforeEnd = task.EndDate;
                                                task.BeforeStart = task.StartDate;
                                                task.BeforeAssignedWork = task.AssignedWork;
                                            }

                                            if ( task.BeforeEnd != DateTime.MinValue && task.BeforeStart != DateTime.MinValue )
                                            {
                                                if ( task.BeforeAssignedWork == default ( float ) )
                                                {
                                                    float numberOfWorkingDays = DateTimeHelpers.GetNumberOfWorkDays ( task.BeforeStart, task.BeforeEnd );
                                                    assignedWorkOld = ( float ) ( numberOfWorkingDays * minutesPerDay );
                                                }
                                                else if ( task.BeforeAssignedWork != default ( float ) )
                                                {
                                                    float numberOfWorkingDays = DateTimeHelpers.GetNumberOfWorkDays ( task.BeforeStart, task.BeforeEnd );
                                                    assignedWorkOld = task.BeforeAssignedWork;
                                                    minutesPerDay = assignedWorkOld / numberOfWorkingDays;
                                                }
                                                if ( assignedWorkOld != default ( float ) )
                                                {
                                                    // convert duration to minutes
                                                    float numberOfWorkingDays = DateTimeHelpers.GetNumberOfWorkDays ( task.BeforeStart, task.BeforeEnd );
                                                    taskDurationOld = ( float ) ( numberOfWorkingDays * minutesPerDay );
                                                    if ( task.BeforeEnd > task.ModifiedDate && task.ModifiedDate >= task.BeforeStart )
                                                    {
                                                        //  minutes completed
                                                        minutesShouldCompletedOld = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( task.BeforeStart, task.ModifiedDate ) * minutesPerDay );
                                                        taskWeightOld = taskDurationOld / ( project.TotalWork + taskDurationOld );
                                                        // individual task related calculation
                                                        // if today still before end date
                                                        if ( minutesShouldCompletedOld <= taskDurationOld )
                                                        {
                                                            taskShouldBeOld = ( minutesShouldCompletedOld / taskDurationOld ) * 100;
                                                        }
                                                        else
                                                        {
                                                            taskShouldBeOld = ( taskDurationOld / taskDurationOld ) * 100;
                                                        }
                                                        //minutes per day      
                                                        taskCompleteOnProjectOld = task.BeforePercentComplete;
                                                        project.ActualWork -= ( taskCompleteOnProjectOld / 100 ) * taskDurationOld;
                                                        project.ProjectedWork -= minutesShouldCompletedOld;
                                                        minutesShouldCompletedOld = minutesShouldCompletedOld != default ( float ) ? ( float ) ( ( ( task.ModifiedDate - task.BeforeStart ).Days - 1 ) * minutesPerDay ) : default ( float );
                                                    }
                                                    else if ( task.BeforeEnd < task.ModifiedDate )
                                                    {
                                                        //  minutes completed
                                                        minutesShouldCompletedOld = taskDurationOld;
                                                        taskWeightOld = taskDurationOld / ( project.TotalWork + taskDurationOld );
                                                        // individual task related calculation
                                                        // if today still before end date
                                                        if ( minutesShouldCompletedOld <= taskDurationOld )
                                                        {
                                                            taskShouldBeOld = ( minutesShouldCompletedOld / taskDurationOld ) * 100;
                                                        }
                                                        else
                                                        {
                                                            taskShouldBeOld = ( taskDurationOld / taskDurationOld ) * 100;
                                                        }
                                                        //minutes per day      
                                                        //taskShouldOnProjectOld = (taskShouldBeOld * taskWeightOld);
                                                        taskCompleteOnProjectOld = task.BeforePercentComplete;
                                                        project.ActualWork -= ( taskCompleteOnProjectOld / 100 ) * taskDurationOld;
                                                        project.ProjectedWork -= minutesShouldCompletedOld;
                                                    }
                                                    else if ( task.ModifiedDate < task.BeforeStart )
                                                    {
                                                        taskShouldBeOld = default ( float );
                                                        //  minutes completed
                                                        minutesShouldCompletedOld = default ( float );
                                                        // convert duration to minutes
                                                        taskDurationOld = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( task.BeforeStart, task.BeforeEnd ) * minutesPerDay );
                                                        taskCompleteOnProjectOld = task.BeforePercentComplete;
                                                        project.ActualWork -= ( taskCompleteOnProjectOld / 100 ) * taskDurationOld;
                                                    }
                                                    // decrement counts
                                                    bool decrementIsLate = ( ( task.BeforeEnd < task.ModifiedDate )
                                                                            && ( task.BeforePercentComplete < 100 ) );
                                                    bool decrementIsSlipping = ( !decrementIsLate
                                                                                && ( task.BeforePercentComplete < taskShouldBeOld ) );
                                                    if ( decrementIsLate )
                                                        numLate--;
                                                    if ( decrementIsSlipping )
                                                        numSlipping--;
                                                }
                                            }
                                        }
                                    }
                                    #endregion
                                }
                                if ( fromTimerJob )
                                {
                                    project.TotalWork = default ( float );
                                    project.ActualWork = default ( float );
                                    project.ProjectedWork = default ( float );
                                }
                                #region Work Stats To Be Added

                                //Get All Project Tasks
                                //Project Duration should be all tasks duration added up
                                foreach ( Work work in tasks )
                                {
                                    if ( work.EditType != EditType.Deleted )
                                    {
                                        if ( work.StartDate != DateTime.MinValue && work.EndDate != DateTime.MinValue )
                                        {
                                            assignedWorkNew = default ( float );
                                            if ( work.AssignedWork == default ( float ) )
                                            {
                                                assignedWorkNew = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( work.StartDate, work.EndDate ) * minutesPerDay );
                                            }
                                            else if ( work.AssignedWork != default ( float ) )
                                            {
                                                assignedWorkNew = work.AssignedWork;
                                            }
                                            if ( assignedWorkNew != default ( float ) )
                                            {
                                                minutesPerDay = assignedWorkNew / DateTimeHelpers.GetNumberOfWorkDays ( work.StartDate, work.EndDate );
                                                projectDurationNew = assignedWorkNew;
                                                project.TotalWork += assignedWorkNew;
                                            }
                                        }
                                    }
                                }
                                foreach ( Work task in tasks )
                                {
                                    if ( task.EditType != EditType.Deleted )
                                    {
                                        if ( task.StartDate != DateTime.MinValue && task.EndDate != DateTime.MinValue )
                                        {
                                            assignedWorkNew = default ( float );
                                            if ( task.AssignedWork == default ( float ) )
                                            {
                                                assignedWorkNew = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( task.StartDate, task.EndDate ) * minutesPerDay );
                                            }
                                            else if ( task.AssignedWork != default ( float ) )
                                            {
                                                assignedWorkNew = task.AssignedWork;
                                                minutesPerDay = assignedWorkNew / DateTimeHelpers.GetNumberOfWorkDays ( task.StartDate, task.EndDate );
                                            }
                                            if ( assignedWorkNew != default ( float ) )
                                            {
                                                // convert duration to minutes
                                                taskDurationNew = assignedWorkNew;
                                                if ( task.EndDate > DateTime.Now && DateTime.Now >= task.StartDate )
                                                {
                                                    //  minutes completed
                                                    minutesShouldCompletedNew = ( float ) ( DateTimeHelpers.GetNumberOfWorkDays ( task.StartDate, DateTime.Now ) * minutesPerDay );
                                                    taskWeightNew = taskDurationNew / project.TotalWork;
                                                    // individual task related calculation
                                                    // if today still before end date
                                                    if ( minutesShouldCompletedNew <= taskDurationNew )
                                                    {
                                                        taskShouldBeNew = ( minutesShouldCompletedNew / taskDurationNew ) * 100;
                                                    }
                                                    else
                                                    {
                                                        taskShouldBeNew = ( taskDurationNew / taskDurationNew ) * 100;
                                                    }
                                                    //minutes per day      
                                                    taskCompleteOnProjectNew = task.PercentageComplete;
                                                    project.ActualWork += ( taskCompleteOnProjectNew / 100 ) * taskDurationNew;
                                                    project.ProjectedWork += minutesShouldCompletedNew;
                                                    //  minutes completed and task should be at by yesterday 
                                                    minutesShouldCompletedNew = minutesShouldCompletedNew != default ( float ) ? ( float ) ( ( DateTimeHelpers.GetNumberOfWorkDays ( task.StartDate, DateTime.Now ) - 1 ) * minutesPerDay ) : default ( float );

                                                }
                                                else if ( task.EndDate < DateTime.Now )
                                                {
                                                    minutesShouldCompletedNew = taskDurationNew;
                                                    taskWeightNew = taskDurationNew / project.TotalWork;
                                                    // individual task related calculation
                                                    // if today still before end date
                                                    if ( minutesShouldCompletedNew <= taskDurationNew )
                                                    {
                                                        taskShouldBeNew = ( minutesShouldCompletedNew / taskDurationNew ) * 100;
                                                    }
                                                    else
                                                    {
                                                        taskShouldBeNew = ( taskDurationNew / taskDurationNew ) * 100;
                                                    }
                                                    //minutes per day      
                                                    taskCompleteOnProjectNew = task.PercentageComplete;
                                                    project.ActualWork += ( taskCompleteOnProjectNew / 100 ) * taskDurationNew;
                                                    project.ProjectedWork += minutesShouldCompletedNew;
                                                }
                                                else if ( task.StartDate > DateTime.Now )
                                                {
                                                    taskShouldBeNew = default ( float );
                                                    minutesShouldCompletedNew = default ( float );
                                                    taskCompleteOnProjectNew = task.PercentageComplete;
                                                    project.ActualWork += ( taskCompleteOnProjectNew / 100 ) * taskDurationNew;
                                                }

                                                // increment counts
                                                bool incrementIsLate = ( ( task.EndDate < DateTime.Now )
                                                                        && ( task.PercentageComplete < 100 ) );
                                                bool incrementIsSlipping = ( !incrementIsLate
                                                                            && ( task.PercentageComplete < taskShouldBeNew ) );
                                                if ( incrementIsLate )
                                                    numLate++;
                                                if ( incrementIsSlipping )
                                                    numSlipping++;
                                            }
                                        }
                                    }
                                }
                                #endregion

                                #endregion
                                //the three lines below are used to make sure no negative numbers are being passed
                                // due to subtracting float number since the ProjectsDA doesn't save negative numbers
                                project.ActualWork = project.ActualWork < 0 ? 0 : project.ActualWork;
                                project.ProjectedWork = project.ProjectedWork < 0 ? 0 : project.ProjectedWork;
                                project.TotalWork = project.TotalWork < 0 ? 0 : project.TotalWork;

                                project.EditType = EditType.Updated;
                                project.TaskStats.Clear ( );
                                project.TaskStats.Add ( numLate > 0 ? numLate.ToString ( ) : "0" );
                                project.TaskStats.Add ( numSlipping > 0 ? numSlipping.ToString ( ) : "0" );

                            }
                            #endregion

                            #region Issue Stats

                            if ( issues.Count > 0 || fromTimerJob )
                            {
                                //•	Issue Statistics
                                //o	Late Issue is where the due date < today and the issue is not closed.
                                //o	High priority is an issue marked as high priority that is not closed.
                                int numLate = 0;
                                if ( project.IssueStats.Count > 0 && !fromTimerJob )
                                    int.TryParse ( project.IssueStats [ 0 ], out numLate );

                                int numHighPriority = 0;
                                if ( project.IssueStats.Count > 1 && !fromTimerJob )
                                    int.TryParse ( project.IssueStats [ 1 ], out numHighPriority );

                                issues.ForEach (
                                    delegate ( Work issue )
                                    {
                                        if ( fromTimerJob )
                                        {
                                            if ( issue.IsLate )
                                                numLate++;

                                            if ( issue.IsHighPriority )
                                                numHighPriority++;
                                        }
                                        else
                                        {
                                            if ( issue.EditType == EditType.Deleted )
                                            {
                                                if ( issue.IsLate )
                                                    numLate--;
                                                if ( issue.IsHighPriority )
                                                    numHighPriority--;
                                            }
                                            else
                                            {
                                                if ( issue.IsLateAdd )
                                                    numLate++;
                                                else if ( issue.IsLateDel )
                                                    numLate--;

                                                if ( issue.IsHighPriorityAdd )
                                                    numHighPriority++;
                                                else if ( issue.IsHighPriorityDel )
                                                    numHighPriority--;
                                            }
                                        }
                                    } );

                                project.EditType = EditType.Updated;
                                project.IssueStats.Clear ( );
                                project.IssueStats.Add ( numLate > 0 ? numLate.ToString ( ) : "0" );
                                project.IssueStats.Add ( numHighPriority > 0 ? numHighPriority.ToString ( ) : "0" );
                            }

                            #endregion
                        }

                        #endregion

                        #region Document Stats

                        if ( fromTimerJob )
                        {
                            List<Document> allDocuments = DocumentModel.GetAllDocuments ( siteCollectionId, webUrl );
                            if ( allDocuments != null && allDocuments.Count > 0 )
                                midori.Documents.AddRange ( allDocuments );
                        }

                        if ( midori.Documents.Count > 0 || fromTimerJob )
                        {
                            //•	Document Statistics
                            //o	New this week is the number of documents created Sunday to Saturday.
                            //o	Updated this week is the number of documents updated Sunday to Saturday.
                            int createdDocs = 0;
                            if ( project.DocumentStats.Count > 0 && !fromTimerJob )
                                int.TryParse ( project.DocumentStats [ 0 ], out createdDocs );

                            int updatedDocs = 0;
                            if ( project.DocumentStats.Count > 1 && !fromTimerJob )
                                int.TryParse ( project.DocumentStats [ 1 ], out updatedDocs );

                            project.EditType = EditType.Updated;

                            foreach ( Document doc in midori.Documents )
                            {
                                if ( !doc.IsFolder )
                                {
                                    if ( fromTimerJob )
                                    {
                                        if ( doc.CreatedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                            createdDocs++;

                                        if ( doc.ModifiedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) && doc.Version > 1 )
                                            updatedDocs++;
                                    }
                                    else
                                    {
                                        if ( doc.IsCreatedThisWeekAdd )
                                            createdDocs++;
                                        if ( doc.IsUpdatedThisWeekAdd )
                                            updatedDocs++;

                                        if ( doc.EditType == EditType.Deleted )
                                        {
                                            if ( doc.CreatedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                createdDocs--;
                                            if ( doc.ModifiedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) && doc.Version > 1 )
                                                updatedDocs--;
                                        }
                                    }
                                }
                            }

                            project.DocumentStats.Clear ( );
                            project.DocumentStats.Add ( createdDocs > 0 ? createdDocs.ToString ( ) : "0" );
                            project.DocumentStats.Add ( updatedDocs > 0 ? updatedDocs.ToString ( ) : "0" );
                        }

                        #endregion

                        #region Discussion Stats

                        if ( fromTimerJob )
                        {
                            // pass empty list of ids to get ALL
                            List<Discussion> allDiscussions = DiscussionModel.GetDiscussionsByIds ( new List<int> ( ), siteCollectionId, webUrl );
                            if ( allDiscussions != null && allDiscussions.Count > 0 )
                                midori.Discussions.AddRange ( allDiscussions );
                        }

                        if ( midori.Discussions.Count > 0 || fromTimerJob )
                        {
                            //•	Discussion Statistics
                            //o	New this week is the number of discussions created Sunday to Saturday.
                            //o	Updated this week is the number of discussions replied to Sunday to Saturday.

                            int createdDiscussions = 0;
                            if ( project.DiscussionStats.Count > 0 && !fromTimerJob )
                                int.TryParse ( project.DiscussionStats [ 0 ], out createdDiscussions );

                            int updatedDiscussions = 0;
                            if ( project.DiscussionStats.Count > 1 && !fromTimerJob )
                                int.TryParse ( project.DiscussionStats [ 1 ], out updatedDiscussions );

                            project.EditType = EditType.Updated;

                            foreach ( Discussion disc in midori.Discussions )
                            {
                                if ( fromTimerJob )
                                {
                                    if ( disc.CreatedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                        createdDiscussions++;
                                    if ( disc.LastCommentDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) && disc.LastCommentDate != disc.CreatedDate )
                                        updatedDiscussions++;
                                }
                                else
                                {
                                    //Decrement createdDiscussions if discussion is created within the last week and is being deleted
                                    if ( disc.EditType == EditType.Deleted )
                                    {
                                        if ( disc.CreatedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                        {
                                            createdDiscussions--;
                                        }
                                        //Decrement updatedDiscussions if discussion is updated within the last week and being deleted
                                        if ( disc.Comments.Count > 0 && ( disc.LastCommentDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) || disc.ModifiedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) ) )
                                        {
                                            updatedDiscussions--;
                                        }
                                        else if ( disc.LastCommentDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) || disc.ModifiedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                        {
                                            updatedDiscussions--;
                                        }
                                    }

                                    //Add to stats
                                    if ( disc.EditType == EditType.Created )
                                        createdDiscussions++;

                                    if ( disc.EditType == EditType.Updated )
                                    {
                                        //check if the update is comiing from adding, editing, or deleting a comment
                                        //if 0 then no comments have been modified, deleted, or created
                                        if ( midori.Comments.Count > 0 )
                                        {
                                            //Decrement updatedDiscussions if comment is updated last week and being deleted.
                                            if ( midori.Comments [ 0 ].EditType == EditType.Deleted )
                                            {
                                                if ( disc.NumberOfComments > 0 )
                                                {
                                                    //if comment before last comment is still within a week
                                                    if ( disc.ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum ) && disc.Comments [ 0 ].ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                        updatedDiscussions--;
                                                }
                                                else
                                                {
                                                    if ( disc.ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum )
                                                            && midori.Comments [ 0 ].ModifiedDate >= DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                        updatedDiscussions--;
                                                }
                                            }
                                            else //Increment updatedDiscussions
                                            {
                                                if ( disc.NumberOfComments > 1 )
                                                {
                                                    if ( disc.ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum ) && disc.Comments [ 1 ].ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                    {
                                                        updatedDiscussions++;
                                                    }
                                                }
                                                else if ( disc.NumberOfComments == 1 )
                                                {
                                                    if ( disc.ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum )
                                                            && midori.Comments [ 0 ].EditType == EditType.Created )
                                                        updatedDiscussions++;
                                                    else if ( midori.Comments [ 0 ].EditType == EditType.Updated
                                                        && disc.LastCommentDate < DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                        updatedDiscussions++;
                                                    else if ( midori.Comments [ 0 ].EditType == EditType.Created && updatedDiscussions == 0 )
                                                        updatedDiscussions++;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if ( disc.Comments.Count > 0 )
                                            {
                                                if ( disc.LastCommentDate < DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                                {
                                                    updatedDiscussions++;
                                                }
                                            }
                                            else if ( disc.ModifiedDate < DateTime.Now.AddDays ( -dayOfWeekNum ) )
                                            {
                                                updatedDiscussions++;
                                            }
                                        }
                                    }
                                }
                            }

                            project.DiscussionStats.Clear ( );
                            project.DiscussionStats.Add ( createdDiscussions > 0 ? createdDiscussions.ToString ( ) : "0" );
                            project.DiscussionStats.Add ( updatedDiscussions > 0 ? updatedDiscussions.ToString ( ) : "0" );
                        }

                        #endregion

                        ProjectsDA.Instance.Save ( project, siteCollectionId, webUrl );
                    }
                }
                else
                    throw new Exception ( "Unable to update Project Statistics on site: " + webUrl + "." );

            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }
        }

        public static void ProjectIsSynched ( Project project, Guid siteCollectionId, string serverRelativeUrl )
        {
            if ( project.EditType == EditType.Updated )
            {
                new ProjectValidator ( ).Validate ( project );

                if ( project.IsValid ( ) )
                {
                    ProjectsDA.Instance.ProjectIsSynched ( project, siteCollectionId, serverRelativeUrl );
                }
            }
        }

        public static Project Save ( Project project, Guid siteCollectionId, string serverRelativeUrl )
        {
            try
            {
                if ( project != null && project.EditType != EditType.Unchanged )
                {
                    // get user permissions for this project
                    if ( project.EditType == EditType.Created )
                        PermissionsModel.GetGlobalPermissionBitsForCurrentUser ( project, project.CreatedBy, siteCollectionId, serverRelativeUrl, false );
                    else
                        PermissionsModel.GetGlobalPermissionBitsForCurrentUser ( project, project.ModifiedBy, siteCollectionId, serverRelativeUrl, false );


                    if ( project.PermissionBitsProjects != null && project.PermissionBitsProjects.Count > 0 )
                    {
                        bool canCreate = project.PermissionBitsProjects [ 0 ];
                        bool canRead = project.PermissionBitsProjects [ 1 ];
                        bool canUpdate = project.PermissionBitsProjects [ 2 ];
                        bool canDelete = project.PermissionBitsProjects [ 3 ];

                        if ( canCreate || canUpdate || canDelete )
                        {
                            new ProjectValidator ( ).Validate ( project );

                            if ( project.IsValid ( ) )
                            {
                                Guid webId = default ( Guid );

                                switch ( project.EditType )
                                {
                                    case EditType.Created:

                                        // if project site provisions, continue actions
                                        if ( CreateProjectSite ( project, canCreate, siteCollectionId, serverRelativeUrl ) )
                                        {
                                            User createdBy = project.CreatedBy;
                                            ProjectStatus initStatus = project.ProjectStatus;
                                            if ( project.ProjectStatus == ProjectStatus.Closed )
                                            {
                                                project.ProjectStatus = ProjectStatus.BeingPlanned;
                                            }
                                            project.ProjectPlanFileUrl = DocumentModel.SaveProjectPlan ( project, siteCollectionId, project.ServerRelativeWebUrl );
                                            project = ProjectsDA.Instance.Save ( project, siteCollectionId, project.ServerRelativeWebUrl );
                                            project.CreatedBy = createdBy;
                                            webId = SPHelpers.GetWebId ( siteCollectionId, project.ServerRelativeWebUrl, false );
                                            TeamModel.CreateInitialProjectTeam ( project, siteCollectionId, project.ServerRelativeWebUrl );
                                            KiiroSolutionControllerHelpers.EnsureProject ( siteCollectionId, project.ServerRelativeWebUrl );
                                            if ( initStatus == ProjectStatus.Closed )
                                            {
                                                project.ProjectStatus = initStatus;
                                                project.EditType = EditType.Updated;
                                                project = ProjectsDA.Instance.Save ( project, siteCollectionId, project.ServerRelativeWebUrl );
                                                project.EditType = EditType.Created;
                                            }
                                            CacheHelper.SetCache ( Constants.CacheKeys.Title, project.Title, siteCollectionId, project.ServerRelativeWebUrl );
                                        }
                                        else
                                            throw new Exception ( "The project web site was not created. Please try again." );
                                        break;

                                    case EditType.Deleted:

                                        CacheHelper.RemoveCache ( Constants.CacheKeys.Title, siteCollectionId, project.ServerRelativeWebUrl );

                                        ProjectsDA.Instance.Save ( project, siteCollectionId, serverRelativeUrl );

                                        List<TeamMember> members = TeamModel.GetTeamMembers ( siteCollectionId, project.ServerRelativeWebUrl );
                                        foreach ( TeamMember member in members )
                                            UserModel.UpdateUserProjectIds ( member.User, siteCollectionId, serverRelativeUrl, project.Id, EditType.Deleted );

                                        LicensingModel.DecrementOpenProjectCount ( siteCollectionId, serverRelativeUrl );

                                        if ( DeleteProjectSite ( project, canDelete, siteCollectionId, project.ServerRelativeWebUrl ) )
                                            project = null;
                                        else
                                            throw new Exception ( "The project web site was not deleted. Please try again." );
                                        break;

                                    case EditType.Updated:

                                        if ( canUpdate )
                                        {
                                            webId = SPHelpers.GetWebId ( siteCollectionId, project.ServerRelativeWebUrl, false );
                                            project.ProjectPlanFileUrl = DocumentModel.SaveProjectPlan ( project, siteCollectionId, project.ServerRelativeWebUrl );
                                            project = ProjectsDA.Instance.Save ( project, siteCollectionId, serverRelativeUrl );
                                            KiiroSolutionControllerHelpers.EnsureProject ( siteCollectionId, project.ServerRelativeWebUrl );

                                            CacheHelper.UpdateCache ( Constants.CacheKeys.Title, project.Title, siteCollectionId, project.ServerRelativeWebUrl );
                                        }
                                        else
                                            throw new KiiroUnauthorizedAccessException ( "Updating Project Failed: Current User is not allowed to Update Project object." );
                                        break;

                                    case EditType.Unchanged:
                                    default:
                                        break;
                                }
                            }
                        }
                        else
                            throw new KiiroUnauthorizedAccessException ( "Updating Project Failed: Current User is not allowed to Create/Update/Delete Project object." );
                    }
                    else
                        throw new KiiroUnauthorizedAccessException ( "Updating Project Failed: Current User is not allowed to Create/Update/Delete Project object." );
                }
            }
            catch ( SPException spEx )
            {
                if ( spEx.ErrorCode == -2130575223 )
                {
                    project.ProjectStatusMessage.Add ( new ValidationMessage ( ValidationSeverity.Validation, "Cannot activate Project. Activating Project will exceed Project Limit." ) );
                }
                else
                {
                    ExceptionManager.HandleException ( spEx, true );
                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return project;
        }

        public static Project UpdateSummaryNote ( Project project, Guid siteCollectionId, string serverRelativeUrl )
        {
            //Passing project server relative web url to get the right group name
            if ( PermissionsModel.CanUserUpdateItem ( project, project.ModifiedBy, EntityType.Project, siteCollectionId, project.ServerRelativeWebUrl ) )
            {
                new ProjectValidator ( ).Validate ( project );
                if ( project.IsValid ( ) )
                {
                    if ( project.EditType == EditType.Updated )
                    {
                        ProjectsDA.Instance.Save ( project, siteCollectionId, serverRelativeUrl );
                    }
                }
            }
            else
                throw new KiiroUnauthorizedAccessException ( "Updating Project Status Note: Access Denied." );

            return project;
        }

        internal static Project UpdateProjectManagerIds ( Project project, Guid siteCollectionId, string serverRelativeUrl, int userId, EditType editType, bool isProjectManager )
        {
            // determine if update is required
            if ( project.ProjectManagerIds.Contains ( userId ) && ( editType == EditType.Deleted || ( editType == EditType.Updated && !isProjectManager ) ) )
            {
                project.ProjectManagerIds.Remove ( userId );
                project.EditType = EditType.Updated;
                ProjectsDA.Instance.Save ( project, siteCollectionId, serverRelativeUrl );
            }
            else if ( ( editType == EditType.Created || editType == EditType.Updated ) && !project.ProjectManagerIds.Contains ( userId ) && isProjectManager )
            {
                project.ProjectManagerIds.Add ( userId );
                project.EditType = EditType.Updated;
                ProjectsDA.Instance.Save ( project, siteCollectionId, serverRelativeUrl );
            }

            return project;
        }
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.Model/ProjectsModel.cs $
 * 
 * 47    2/08/10 5:04p Camerons
 * 
 * 46    1/25/10 1:17p Camerons
 * 
 * 45    1/12/10 12:03p Camerons
 * 
 * 44    26/11/09 10:35a Camerons
 * 
 * 43    17/11/09 6:46p Camerons
 * 
 * 42    8/11/09 3:00p Camerons
 * 
 * 41    31/10/09 1:05p Camerons
 * 
 * 40    30/10/09 6:21p Camerons
 * 
 * 39    10/27/09 5:53p Adamw
 * accomodations for moving plan file to document library:
 * -moved saving of plan out of ProjectsDA and into DocumentModel -->
 * DocumentDA
 * -added call to save project plan file to document library from
 * ProjectsModel
 * -changed New/Edit Project pages to pass in Attachment collection
 * 
 * 38    10/23/09 2:11p Adamw
 * 
 * 37    21/10/09 3:06p Shafia
 * using CacheKeys constants instead of hard coding key names
 * 
 * 36    15/10/09 9:34a Camerons
 * 
 * 35    7/10/09 11:47a Shafia
 * 
 * 34    7/10/09 11:42a Shafia
 * Set project status to being planned  create project then set it back to
 * the initial status to enable adding initial team members to the project
 * even when project created as closed
 * 
 * 33    10/06/09 1:10p Adamw
 * fix for enabling WPSandox on Projects
 * 
 * 32    1/10/09 3:28p Camerons
 * Load Expanded ADGroups into drop down on NewTeamMember. Create user on
 * demand if selected user doesn't exist.
 * 
 * 31    30/09/09 9:30a Shafia
 * 
 * 30    29/09/09 12:40p Shafia
 * Implemented WebPart at project level
 * 
 * 29    23/09/09 4:46p Camerons
 * Fixed some old code dealing with User.ManageProjects
 * 
 * 28    22/09/09 6:03p Camerons
 * Code changes to support creating user on demand - version 1.1.x.x
 * 
 * 27    14/09/09 4:13p Shafia
 * The remove stat section was using ".AssignedWork" rather than
 * ".BeforeAssginedWork". This have been changed.
 * Due to rounding the percentages comes as small negative decimal (etc:
 * -0.00000124) which causes it not to be saved when it reaches the
 * ProjectsDA. This have been changed to validate if negative set to 0
 * 
 * 26    11/09/09 3:05p Camerons
 * Use Project.ServerRelativeWebUrl when retreiving TeamMembers during
 * project deletion
 * 
 * 25    31/08/09 11:56a Camerons
 * 
 * 24    19/08/09 1:22p Camerons
 * RE: LicenseKey error Gene was experiencing on nform.mykiiro.com -->
 * Also addded missing null check for Project.ModifiedBy property in
 * GetByServerRelativeWebUrl method
 * 
 * 23    8/18/09 6:10p Camerons
 * RE: LicenseKey error Gene was experiencing on nform.mykiiro.com -->
 * Added missing null check for project entity in
 * GetByServerRelativeWebUrl method
 * 
 * 22    17/08/09 10:29a Camerons
 * Added check-in comments section
 * 
 ************************************************************************/